package br.com.ericogr.hanoi.entidade;

import java.util.ArrayList;
import java.util.List;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import br.com.ericogr.hanoi.Aplicativo;
import br.com.ericogr.hanoi.R;
import br.com.ericogr.hanoi.controle.EventoControle;
import br.com.ericogr.hanoi.controle.EventoControle.Touch;
import br.com.ericogr.hanoi.database.Pontuacao;

public class EntidadeHanoi implements Entidade {
    private static final long serialVersionUID = 1L;
    
    private transient int mScreenWidth;
    private transient int mScreenHeight;
    private List<Prato> mPratos;
    private transient Paint mPaint;
    
    private Prato mPratoMovido;
    private int mXPratoMovido;
    private int mYPratoMovido;
    
    private boolean mFimDeJogo;
    private long mTempo;
    
    private Pontuacao mPontuacao;
    
    private boolean mInicializado;
    
    private transient JogoListener mJogoListener;
    
    private static final float PRATO_MAIOR_LARGURA_PERC = .9f;
    private static final float PRATO_MENOR_LARGURA_PERC = .2f;
    private static final float PRATO_ALTURA_PERC = .03f;
    
    public EntidadeHanoi(int quantidadeDePratos) {
        mPaint = new Paint();
        mFimDeJogo = false;
        mPontuacao = new Pontuacao(quantidadeDePratos, 0, 0);
        
        inicializacao();
    }
    
    @Override
    public void configuraResolucao(int screenWidth, int screenHeight) {
        mScreenWidth = screenWidth;
        mScreenHeight = screenHeight;
    }
    
    @Override
    public void setJogoListener(JogoListener jogoListener) {
        mJogoListener = jogoListener;
    }
    
    protected void fireFimDeJogo(Pontuacao pontuacao) {
        if (mJogoListener != null) {
            mJogoListener.fimDeJogo(pontuacao);
        }
    }

    private void inicializacao() {
        if (!mInicializado) {
            mTempo = System.currentTimeMillis();
            mPratos = new ArrayList<Prato>();
            
            for (int i = 0; i < mPontuacao.getPratos(); i++) {
                mPratos.add(new Prato(Prato.BASE_ESQUERDA, i, i));
            }
            
            mInicializado = true;
        }
    }

    @Override
    public void processamentoLogico(List<Entidade> entidade) {
        if (!mFimDeJogo && isFimDeJogo()) {
            fireFimDeJogo(mPontuacao);
        }
        
        mFimDeJogo = isFimDeJogo();
        
        if (mFimDeJogo) {
            if (mPontuacao.isMovimentosMinimosAtingido()) {
                mPaint.setColor(Color.GREEN);
            }
            else {
                mPaint.setColor(Color.RED);
            }
        }
        else {
            int tempoDecorrido = (int)((System.currentTimeMillis() - mTempo) / 1000);
            mPontuacao.setTempo(tempoDecorrido);
            mPaint.setColor(Color.BLUE);
        }
    }

    @Override
    public void processamentoGrafico(Canvas canvas) {
        int x1, y1, x2, y2;
        
        for (Prato p : mPratos) {
            if (mPratoMovido == null || mPratoMovido != p || !mPratoMovido.isVisivel()) {
                x1 = getXPrato(p.getBase(), p.getOrdem());
                y1 = getYPrato(p.getAltura());
                x2 = x1 + getLarguraPrato(p.getOrdem());
                y2 = y1 + getAlturaDoPrato();
                canvas.drawRect(x1, y1, x2, y2, mPaint);
            }
            else {
                x1 = mXPratoMovido - (getLarguraPrato(mPratoMovido.getOrdem()) >> 1);
                y1 = mYPratoMovido - (getAlturaDoPrato() * 2);
                x2 = x1 + getLarguraPrato(mPratoMovido.getOrdem());
                y2 = y1 + getAlturaDoPrato();
                canvas.drawRect(x1, y1, x2, y2, mPaint);
            }
        }
        
        desenharInformacoes(canvas);
    }
    
    private void desenharInformacoes(Canvas canvas) {
        String info = Aplicativo.getContext().getString(
                R.string.informacoes_jogo,
                new Object[] {mPontuacao.getMovimentos(), mPontuacao.getTempo()});
        
        desenhaTexto(canvas, 10, 10, info, 3);
    }

    @Override
    public void processamentoEvento(EventoControle eventoControle) {
        if (!mFimDeJogo) {
            mXPratoMovido = (int)eventoControle.getX();
            mYPratoMovido = (int)eventoControle.getY();
            
            int base = getBasePelaPosicaoX(mXPratoMovido);
            
            if (eventoControle.getTouch() == Touch.PRESSIONADO) {
                Prato p = getPrato(base);
                
                if (p != null) {
                    mPratoMovido = p;
                    mPratoMovido.setVisivel(true);
                }
            }
            else if (eventoControle.getTouch() == Touch.SOLTO && mPratoMovido != null) {
                if (movimentoValido(mPratoMovido, base)) {
                    mPratoMovido.setAltura(getAltura(base) + 1);
                    mPratoMovido.setBase(base);
                    mPontuacao.incrementaMovimento();
                }
                
                mPratoMovido.setVisivel(false);
            }
        }
    }
    
    private int getBasePelaPosicaoX(int x) {
        int ret = x / getLarguraBase();
        
        ret = Math.min(2, ret);
        ret = Math.max(0, ret);
        
        return ret;
    }

    private void desenhaTexto(Canvas canvas, int x, int y, String texto, int tamanho) {
        Rect rect = new Rect();
        
        int prop = mScreenWidth * 5 / 320 * tamanho;
        
        mPaint.setTextSize(prop);
        mPaint.getTextBounds(texto, 0, texto.length(), rect);
        
        canvas.drawText(texto, x, y + rect.height() + 10, mPaint);
    }
    
    private boolean isFimDeJogo() {
        for (Prato prato : mPratos) {
            if (prato.getBase() != Prato.BASE_DIREITA) {
                return false;
            }
        }
        
        return true;
    }
    
    private boolean movimentoValido(Prato mPratoMovido, int base) {
        Prato prato = getPrato(base);
        
        return prato == null || mPratoMovido.getOrdem() > prato.getOrdem();
    }

    private int getAltura(int base) {
        Prato prato = getPrato(base);
        
        if (prato == null) {
            return -1;
        }
        else {
            return prato.getAltura();
        }
    }
    
    private Prato getPrato(int base) {
        Prato ret = null;
        
        for (Prato p : mPratos) {
            if (p.getBase() == base) {
                if (ret == null || p.getOrdem() > ret.getOrdem()) {
                    ret = p;
                }
            }
        }
        
        return ret;
    }
    
    private int getLarguraBase() {
        int ret = mScreenWidth / 3;
        
        return ret;
    }
    
    private int getAlturaDoPrato() {
        return (int)(mScreenHeight * PRATO_ALTURA_PERC);
    }
    
    private int getLarguraDoMaiorPrato() {
        int ret = (int)(getLarguraBase() * PRATO_MAIOR_LARGURA_PERC);
        
        return ret;
    }
    
    private int getLarguraDoMenorPrato() {
        int ret = (int)(getLarguraBase() * PRATO_MENOR_LARGURA_PERC);
        
        return ret;
    }
    
    private int getLarguraPrato(int ordem) {
        int prop = (getLarguraDoMaiorPrato() - getLarguraDoMenorPrato()) / (mPontuacao.getPratos() - 1);
        int ret = getLarguraDoMaiorPrato() - (ordem * prop);
        
        return ret;
    }
    
    private int getXPrato(int base, int ordem) {
        int ret = 0;
        
        ret = (getLarguraBase() - getLarguraPrato(ordem)) >> 1;
        ret = ret + (base * getLarguraBase());
        
        return ret;
    }
    
    private int getYPrato(int altura) {
        int ret = 0;
        
        ret = mScreenHeight - ((altura + 1) * getAlturaDoPrato());
        
        return ret;
    }

}